/* stiger, 20090425, for cnchdk */

#include "font.h"
#include "stdlib.h"
#include "codepages.h"
#include "font_builtin.h"

static const char *RBF_HDR_MAGIC = "\xE0\x0E\xF0\x0D\x03\x00\x00\x00";

#define MAX_FONT_SIZE 24*20

//ci: char index
struct s_newfont_ci
{
	unsigned short start_code;
	unsigned char count;
	unsigned char width;
	unsigned int offset;
};

#define MAX_CD_CACHE 256 //this will hold RBF data, so don't smaller than 256
struct s_newfont_cache
{
	unsigned short code; //for RBF data, it mean the font width.
	unsigned char data[MAX_FONT_SIZE/8];
};

struct s_newfont
{
	int height;
	int max_width;
	int index_count;
	struct s_newfont_ci *ci;

	int cache_count;
	struct s_newfont_cache cache_data[MAX_CD_CACHE];
	int cache_lastupdate;

	int fd; //fd >= 0, use newfont file,
	//fd == -1: use builtin font, unicode encoding
	//fd == -2: rbf font file, always the same encoding as lng file
}newfont;

struct s_newfont_char
{
	struct s_newfont_ci *ci;
	unsigned char data[MAX_FONT_SIZE*2/8];
};

int newfont_height(void)
{
	return newfont.height;
}

unsigned short newfont_getc(const char *str, int *len){
	if(newfont.fd == -2){
		//if rbf file, then same encoding
		*len = 1;
		unsigned char c = str[0];
		return c;
	}else{
		return codepage_getc(str, len);
	}
}

//for external font, get char data, return: width
static int newfont_find_cd(unsigned short code, struct s_newfont_char *c)
{
	int i;
	unsigned char *data;
	int j,k;

	for(i=0;i<newfont.index_count;i++){
		if(code >= newfont.ci[i].start_code && code < (newfont.ci[i].start_code + newfont.ci[i].count) ){
			//read from cache
			for(j=0; j<newfont.cache_count; j++){
				if(newfont.cache_data[j].code == code){
					for(k=0; k<(((newfont.ci[i].width+7)>>3) * newfont.height); k++){
						c->data[k] = newfont.cache_data[j].data[k];
					}
					break;
				}
			}
			//if not find in cache, read from SD card
			if(j == newfont.cache_count && newfont.fd >= 0){
        		lseek(newfont.fd, newfont.ci[i].offset + ((newfont.ci[i].width+7)>>3)*newfont.height*(code-newfont.ci[i].start_code), SEEK_SET);
				read(newfont.fd, c->data, ((newfont.ci[i].width+7)>>3) * newfont.height );

				//update cache
				if(newfont.cache_count < MAX_CD_CACHE){
					newfont.cache_lastupdate = newfont.cache_count;
					newfont.cache_count ++;
				}else{
					newfont.cache_lastupdate ++;
					if(newfont.cache_lastupdate >= MAX_CD_CACHE) newfont.cache_lastupdate = 0;
				}

				newfont.cache_data[ newfont.cache_lastupdate ].code = code;
				for(k=0; k<(((newfont.ci[i].width+7)>>3) * newfont.height) && k<(MAX_FONT_SIZE/8); k++){
					newfont.cache_data[ newfont.cache_lastupdate ].data[k] = c->data[k];
				}
			}
			c->ci = &newfont.ci[i];
			return newfont.ci[i].width;
		}
	}

	return 0;

}

int newfont_char_width(unsigned short ch)
{
	int i = 0;

	if(newfont.fd == -1){
		//if use built-in font_data
		while (font_data[i].charcode != -1) {
	    	if (font_data[i].charcode == ch) {
				return 8;
			}
			i++;
		}
		return 0;
	}else if(newfont.fd == -2){
		//use rbf file
		if(ch > 255) return 0;
		return newfont.cache_data[ch].code;
	}

	//else, use external font
	for(i=0;i<newfont.index_count;i++){
		if(ch >= newfont.ci[i].start_code && ch < (newfont.ci[i].start_code + newfont.ci[i].count) ){
			return newfont.ci[i].width;
		}
	}
	return 0;

}


int newfont_str_width(const char *str)
{
	int l=0;
	int cl;
	unsigned short c;

	while(*str){
		c = newfont_getc(str, &cl);
		str += cl;

        l+=newfont_char_width(c);
	}

    return l;
}

static struct s_newfont_char nf_c; //don't in stack to save stack size

int newfont_draw_char(int x, int y, unsigned short ch, color cl) {
    int i=0, xx, yy;

	if(newfont.fd == -1){
		//if use built-in font_data
		while (font_data[i].charcode != -1) {
	    	if (font_data[i].charcode == ch) {
	    		for (yy=0; yy<newfont.height; ++yy) {
	        		for (xx=0; xx<8; ++xx) {
	            		draw_pixel(x+xx ,y+yy, (font_data[i].data[yy] & (1<<(8-xx))) ? cl&0xff : cl>>8);
	        		}
	    		}
	        	return 8;
			}
	    	++i;
		}
		return 0;
	}else if(newfont.fd == -2){
		//use rbf font file
		if(ch > 255) return 0;
	   	for (yy=0; yy<newfont.height; ++yy) {
	     	for (xx=0; xx<newfont.cache_data[ch].code; ++xx) {
               	draw_pixel(x+xx ,y+yy, (newfont.cache_data[ch].data[yy*((newfont.cache_data[ch].code+7)>>3)+xx/8] & (1<<(xx%8)))? cl&0xff : cl>>8);
	       	}
	   	}
		return newfont.cache_data[ch].code;
	}else{
		//else if use external font
		if( newfont_find_cd(ch, &nf_c) ){
        	for (yy=0; yy<newfont.height; ++yy) {
            	for (xx=0; xx<nf_c.ci->width; ++xx) {
                	draw_pixel(x+xx ,y+yy, (nf_c.data[yy*((nf_c.ci->width+7)>>3)+xx/8] & (1<<(xx%8)))? cl&0xff : cl>>8);
            	}
        	}
    		return nf_c.ci->width;
    	}
		return 0;
	}
}

int newfont_draw_string(int x, int y, const char *str, color cl) {
    int l=0;
    const char *s=str; 
	unsigned short c;
	int clen;

	while(*s){
		c = newfont_getc(s, &clen);
		s += clen;

		l += newfont_draw_char( x+l, y, c, cl);
	}

    return l; 
}

int newfont_draw_string_len(int x, int y, int len, const char *str, color cl) {
    int l=0, yy;
    const char *s=str; 
	unsigned short c;
	int clen;
	int i;

	while(*s){
		c = newfont_getc(s, &clen);
		s += clen;

		if(l + newfont_char_width(c) <= len){
			l += newfont_draw_char( x+l, y, c, cl);
		}
	}

    for (; l<len; ++l) {
        for (yy=y; yy<y+newfont.height; ++yy) {
            draw_pixel(x+l, yy, cl>>8);
        }
    }

    return l;
}

int newfont_draw_string_right_len(int x, int y, int len, const char *str, color cl) {
    int l=0, i, yy;
    const char *s=str;
	unsigned short c;
	int clen;

	while(*s){
		c = newfont_getc(s, &clen);
		s += clen;

		i = newfont_char_width(c);
		if(l + i > len) break;
		l += i;
	}

    l=len-l;

    for (i=l-1; i>=0; --i) {
        for (yy=y; yy<y+newfont.height; ++yy) {
            draw_pixel(x+i, yy, cl>>8);
        }
    }

	s = str;

	while(*s){
		c = newfont_getc(s, &clen);
		s += clen;

		if(l + newfont_char_width(c) <= len){
			l += newfont_draw_char( x+l, y, c, cl);
		}
	}

    return l;
}

int newfont_draw_string_center_len(int x, int y, int len, char symbol, const char *str, color cl) { 
    int l=0, strLen=0, i, yy, rund=2; 
    const char *s=str; 
	unsigned short c;
	int clen;

	while(*s){
		c = newfont_getc(s, &clen);
		s += clen;

		i = newfont_char_width(c);
		if(l + i > len) break;
		l += i;
	}

	//TODO: what is symbol ? not supported
	strLen = l;
    l=(len-strLen)/2; 

    for (i=0; i<=l; i++) {
        for (yy=y+rund; yy<y+newfont.height; ++yy) {
            draw_pixel(x+i, yy, cl>>8);
            draw_pixel(x+len-i-1, yy, cl>>8);
        }
        if (rund>0) rund--;
    }

	//TODO: what is symbol ? not supported
	/*
    if (symbol!=0x0 && conf.menu_symbol_enable) {
      l+=rbf_draw_symbol(x+l,y,symbol,cl);
      l+=rbf_draw_char(x+l, y, ' ', cl);
    }
	*/

	s = str;
	while(*s){
		c = newfont_getc(s, &clen);
		s += clen;

		i = newfont_draw_char( x+l, y, c, cl);
		if(l + i > len) break;
		l += i;
	}

    return l; 
}

int newfont_draw_symbol(int x, int y, int ch, color cl) {
		return 0;
}

void newfont_set_codepage(int codepage) {
}

int newfont_load_symbol(char *file) {
		return 0;
}

int newfont_load(char *file) {
    unsigned char buf[64];

	//free old file
	if(newfont.fd >= 0)
		close(newfont.fd);

    newfont.fd = open(file, O_RDONLY, 0777);
    if (newfont.fd>=0){
	    if( read(newfont.fd, buf, 4)==4 /* NEWfont Magic number */ && memcmp(buf, "HE01", 4)==0) {

			//it's newfont file
			if(newfont.ci){
				free(newfont.ci);
				newfont.ci = 0;
			}
			newfont.index_count = 0;
			newfont.cache_count = 0;
		
		    read(newfont.fd, buf, 4);
			newfont.height = buf[0];
		    read(newfont.fd, &newfont.index_count, 4);
		    read(newfont.fd, buf, 4);
		
			newfont.ci = malloc(sizeof(struct s_newfont_ci) * newfont.index_count);
		
			if(newfont.ci == NULL){
				close(newfont.fd);
				newfont.fd = -1;
				return 0;
			}
			read(newfont.fd, newfont.ci, sizeof(struct s_newfont_ci) * newfont.index_count );

			return 1;
	    }else{
	    	if( read(newfont.fd, buf + 4, 4)==4 /* NEWfont Magic number */ && memcmp(buf, RBF_HDR_MAGIC, 8)==0) {
	
     			int _wmapAddr;
     			int _cmapAddr;
     			int charFirst, charLast;
				int i;
				char wTable;
     			int charSize;
				int xx,yy;

				//it's rbf file
				if(newfont.ci){
					free(newfont.ci);
					newfont.ci = 0;
				}

		        read(newfont.fd, buf, 64);
		        read(newfont.fd, &charSize , 4);
		        read(newfont.fd, buf   , 4);
		        read(newfont.fd, &newfont.height   , 4);
		        read(newfont.fd, buf , 4);
		        read(newfont.fd, &charFirst, 4);
		        read(newfont.fd, &charLast , 4);
		        read(newfont.fd, buf, 4);
		        read(newfont.fd, &_wmapAddr, 4);
		        read(newfont.fd, &_cmapAddr, 4);
		        read(newfont.fd, buf  , 4);
		        read(newfont.fd, buf  , 4);

				memset(newfont.cache_data, 0, sizeof(newfont.cache_data));
			
			    lseek(newfont.fd, _wmapAddr, SEEK_SET);
			    for (i=charFirst; i<=charLast; i++) {
			        read(newfont.fd, &wTable, 1);
					if( (wTable + 7)*newfont.height > MAX_FONT_SIZE ) wTable = MAX_FONT_SIZE/newfont.height - 7;
					newfont.cache_data[i].code = wTable;
			    }
			
			    lseek(newfont.fd, _cmapAddr, SEEK_SET);
			    for (i=charFirst; i<=charLast; i++) {
			        read(newfont.fd, buf, charSize);
        			for (yy=0; yy<newfont.height; ++yy) {
		            	for (xx=0; xx<newfont.cache_data[i].code; ++xx) {
							if( buf[yy*charSize/newfont.height+xx/8] & (1<<(xx&7)) )
                				newfont.cache_data[i].data[yy*((newfont.cache_data[i].code + 7)>>3) + xx/8] |= (1 << (xx&7));
						}
					}
            	}

			    close(newfont.fd);
				newfont.fd = -2;

			    return 1;
	    	}else{
				close(newfont.fd);
			}
	    }
	}
	newfont.fd = -1;
	return 0;
}

void newfont_load_from_8x16(unsigned char font[256][16]) {
    int i, c, b;

    newfont.height    = 16;
	/*
    rbf_font.charSize  = 16;
    rbf_font.maxWidth  = 8;
    rbf_font.charFirst = 0;
    rbf_font.charLast  = 255;
    rbf_font.width = 8 * rbf_font.charSize / rbf_font.height;
    rbf_font.charCount = rbf_font.charLast - rbf_font.charFirst + 1;
*/

	if(newfont.ci){
		free(newfont.ci);
		newfont.ci = 0;
	}
	if(newfont.fd >= 0){
		close(newfont.fd);
		newfont.fd = -1;
	}

	newfont.index_count = 0;
	newfont.cache_count = 0;
}

void  newfont_init(void)
{
	memset(&newfont, 0, sizeof(newfont));
	newfont.fd = -1;
}
